<!--
  Form子项组件                zls-2022/06/01
      Attributes
          1、prop             验证字段(如若该字段需要校验，则必填！)

      slot
      
      Demo==>
          请参考 Form 表单组件
-->
<template>
  <div class="z-formItem">
    <slot></slot>
  </div>
</template>

<script>
export default { name: 'z-formItem' }
</script>

<script setup>
import schema from 'async-validator' // npm i async-validator下载表单校验库
import { ref, provide, inject, computed, onUnmounted, onMounted, getCurrentInstance } from 'vue'
const global = getCurrentInstance().appContext.config.globalProperties
const form = inject('form')
const props = defineProps({
  // 验证字段
  prop: {
    type: String,
    default: ''
  }
})

// 当前需要校验项的值
const fieldValue = computed(() => {
  return form.model[props.prop]
})

const isRequired = ref(false) // 当前项是否需要验证
const validateState = ref('') // 是否校验成功
const validateMessage = ref('') // 校验失败文字
// 将formItem实例传递给子组件
provide(
  'formItem',
  computed(() => {
    return {
      isRequired: isRequired.value,
      validateState: validateState.value,
      validateMessage: validateMessage.value,
      prop: props.prop
    }
  })
)

// 组件渲染时，将实例缓存在 Form 中
onMounted(() => {
  // 如果没有传入 prop，则无需校验，也就无需缓存
  if (props.prop) {
    global.$bus.emit('formItemMounted', {
      prop: props.prop,
      validation: validation
    })
    setItemRules()
  }
})
// 组件销毁前，将实例从 Form 的缓存中移除
onUnmounted(() => {
  global.$bus.emit('formItemDestroy', {
    prop: props.prop,
    validation: validation
  })
})

// 从 Form组件 的 rules 属性中，获取当前 FormItem 的校验规则
function getItemRules() {
  const itemRules = form.rules && props.prop ? form.rules[props.prop] : []
  return itemRules
}

// 绑定事件 进行是否 required 校验
function setItemRules() {
  let rules = getItemRules() // 拿到父组件过滤后当前需要使用的规则
  if (rules && rules.length) {
    isRequired.value = rules.some(rule => rule.required)
  }
  global.$bus.on('formItem-blur', val => validation('blur', val)) // blur 事件
  global.$bus.on('formItem-change', val => validation('change', val)) // change 事件
}

/**
 * 校验函数
 * @param trigger 校验类型 blur / change
 * @param callback 回调函数
 */
function validation(trigger, val, callback = () => {}) {
  // 1、如果当前项并没有prop，则无需校验
  if (val === 'noProp') return
  // 2、校验字段存在且和当前项的字段不同，则无需校验（保证校验当前项）
  if (val && val !== props.prop) return
  // 3、过滤出 blur 和 change下的当前项校验规则
  let rules = getFilteredRule(trigger)
  if (!rules || rules.length === 0) return
  // async-validator的使用形式
  var validator = new schema({ [props.prop]: rules })
  // firstFields: true 只会校验一个
  validator.validate({ [props.prop]: fieldValue.value }, { firstFields: true }, (errors, fields) => {
    validateState.value = !errors ? 'success' : 'error'
    validateMessage.value = errors ? errors[0].message : ''
    callback(validateMessage.value)
  })
}

// 只支持 blur 和 change，所以过滤出符合要求的 rule 规则
function getFilteredRule(trigger) {
  let rules = getItemRules()
  return rules && rules.filter(res => !res.trigger || res.trigger.indexOf(trigger) !== -1)
}
</script>
